/*
    https://wangdoc.com/javascript/types/number.html
    数值
 */
1 === 1.0; // true

0.1 + 0.2 === 0.3; // false

0.3 / 0.1; // 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1);  // false

// special value
// positive zero && negative zero
-0 === +0;  // true
0 === -0;   // true
0 === +0;   // true

// 几乎所有场合，正零和负零都会被当作正常的0
+0;
-0;
(-0).toString();
(+0).toString();

// 唯一有区别的场合是，+0或-0当作分母，返回的值是不相等的。
(1 / +0) === (1 / -0); // false
// 上面的代码之所以出现这样结果，是因为除以正零得到+Infinity，除以负零得到-Infinity，这两者是不相等的（关于Infinity详见下文）

// NaN, Not a Number, usually occur when parse str to number but the str is not a number str;
5 - 'x';
Math.acos(2);
Math.log(-1);
Math.sqrt(-1);
0 / 0; // 这个还是挺有趣的,毕竟在Java中,除以0你将得到一个ArithmeticException(算术异常)

// 需要注意的是，NaN不是独立的数据类型，而是一个特殊数值，它的数据类型依然属于Number，使用typeof运算符可以看得很清楚。
typeof NaN;

// NaN不等于任何值，包括它本身
NaN === NaN;

// 数组的indexOf方法内部使用的是严格相等运算符，所以该方法对NaN不成立
[NaN].indexOf(NaN);

// NaN在布尔运算时被当作false
Boolean(NaN);

// NaN与任何数（包括它自己）的运算，得到的都是NaN
NaN + 32;
NaN - 32;
NaN * 32;
NaN / 32;

// Infinity
// Infinity表示“无穷”,用来表示两种场景.一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity
// 场景一
Math.pow(2, 1024);

// 场景二
console.log(0 / 0); // NaN
console.log(1 / 0); // Infinity

// Infinity有正负之分，Infinity表示正的无穷，-Infinity表示负的无穷
Infinity === -Infinity;
1 / -0;
-1 / -0;

// Infinity大于一切数值（除了NaN），-Infinity小于一切数值（除了NaN）
Infinity > 1000;
-Infinity < -1000;

//（2）运算规则
// Infinity的四则运算，符合无穷的数学计算规则。
5 * Infinity; // Infinity
5 - Infinity; // -Infinity
Infinity / 5; // Infinity
5 / Infinity; // 0;

// 0乘以Infinity，返回NaN；0除以Infinity，返回0；Infinity除以0，返回Infinity
0 * Infinity;  // NaN
0 / Infinity;  // 0
Infinity / 0;  // Infinity

// Infinity减去或除以Infinity，得到NaN
Infinity - Infinity;   // NaN
Infinity / Infinity;   // NaN

// Infinity与null计算时，null会转成0，等同于与0的计算
null * Infinity;   // NaN
null / Infinity;   // 0
Infinity / null;   // Infinity

// Infinity与undefined计算，返回的都是NaN
undefined + Infinity // NaN
undefined - Infinity // NaN
undefined * Infinity // NaN
undefined / Infinity // NaN
Infinity / undefined // NaN

// 与数值相关的全局方法
// parseInt方法用于将字符串转为整数
parseInt('123') // 123

// 如果字符串头部有空格，空格会被自动去除
parseInt('   81') // 81

// 如果parseInt的参数不是字符串，则会先转为字符串再转换
parseInt(1.23) // 1
// 等同于
parseInt('1.23') // 1

// 字符串转为整数的时候，是一个个字符依次转换，如果遇到不能转为数字的字符，就不再进行下去，返回已经转好的部分
parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12
parseInt('15e2') // 15
parseInt('15px') // 15

// 如果字符串的第一个字符不能转化为数字（后面跟着数字的正负号除外），返回NaN
parseInt('abc') // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1

// 所以，parseInt的返回值只有两种可能，要么是一个十进制整数，要么是NaN

// 如果字符串以0x或0X开头，parseInt会将其按照十六进制数解析
parseInt('0x10') // 16

// 如果字符串以0开头，将其按照10进制解析
parseInt('011'); // 11

// 对于那些会自动转为科学计数法的数字，parseInt会将科学计数法的表示方法视为字符串，因此导致一些奇怪的结果
parseInt(1000000000000000000000.5) // 1
// 等同于
parseInt('1e+21') // 1

parseInt(0.0000008) // 8
// 等同于
parseInt('8e-7') // 8

// （2）进制转换
// parseInt方法还可以接受第二个参数（2到36之间），表示被解析的值的进制，返回该值对应的十进制数。默认情况下，parseInt的第二个参数为10，即默认是十进制转十进制。
parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000
// 下面是转换指定进制的数的例子
parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512
// 上面代码中，二进制、六进制、八进制的1000，分别等于十进制的8、216和512。这意味着，可以用parseInt方法进行进制的转换

// 如果第二个参数不是数值，会被自动转为一个整数。这个整数只有在2到36之间，才能得到有意义的结果，超出这个范围，则返回NaN。
// 如果第二个参数是0、undefined和null，则直接忽略
parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10

// 如果字符串包含对于指定进制无意义的字符，则从最高位开始，只返回可以转换的数值。如果最高位无法转换，则直接返回NaN
parseInt('1546', 2) // 1
parseInt('546', 2) // NaN
// 上面代码中，对于二进制来说，1是有意义的字符，5、4、6都是无意义的字符，所以第一行返回1，第二行返回NaN

// 前面说过，如果parseInt的第一个参数不是字符串，会被先转为字符串。这会导致一些令人意外的结果
parseInt(0x11, 36) // 43
parseInt(0x11, 2) // 1

// 等同于
parseInt(String(0x11), 36)
parseInt(String(0x11), 2)

// 等同于
parseInt('17', 36)
parseInt('17', 2)

// 上面代码中，十六进制的0x11会被先转为十进制的17，再转为字符串。然后，再用36进制或二进制解读字符串17，最后返回结果43和1
parseInt(011, 2) // NaN
// 等同于
parseInt(String(011), 2)
// 等同于
parseInt(String(9), 2)
// 上面代码中，第一行的011会被先转为字符串9，因为9不是二进制的有效字符，所以返回NaN。如果直接计算parseInt('011', 2)，011则是会被当作二进制处理，返回3。

// 5.2 parseFloat()
// parseFloat方法用于将一个字符串转为浮点数
parseFloat('3.14'); // 3.14

// 如果字符串符合科学计数法，则会进行相应的转换
parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14

// 如果字符串包含不能转为浮点数的字符，则不再进行往后转换，返回已经转好的部分。
parseFloat('3.14more non-digit characters') // 3.14

// parseFloat方法会自动过滤字符串前导的空格
parseFloat('\t\v\r12.34\n ') // 12.34

// 如果参数不是字符串，或者字符串的第一个字符不能转化为浮点数，则返回NaN
parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN

// 上面代码中，尤其值得注意，parseFloat会将空字符串转为NaN
// 这些特点使得parseFloat的转换结果不同于Number函数
parseFloat(true)  // NaN
Number(true) // 1

parseFloat(null) // NaN
Number(null) // 0

parseFloat('') // NaN
Number('') // 0

parseFloat('123.45#') // 123.45
Number('123.45#') // NaN

// isNaN()
// isNaN方法可以用来判断一个值是否为NaN
isNaN(NaN); // true
isNaN(123); // false

// 但是，isNaN只对数值有效，如果传入其他值，会被先转成数值。
// 比如，传入字符串的时候，字符串会被先转成NaN，所以最后返回true，这一点要特别引起注意。也就是说，isNaN为true的值，有可能不是NaN，而是一个字符串
isNaN('Hello') // true
// 相当于
isNaN(Number('Hello')) // true

// 出于同样的原因，对于对象和数组，isNaN也返回true
isNaN({}) // true
// 等同于
isNaN(Number({})) // true

isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true

// 但是，对于空数组和只有一个数值成员的数组，isNaN返回false
isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false
// 上面代码之所以返回false，原因是这些数组能被Number函数转成数值，请参见《数据类型转换》一章
// 因此，使用isNaN之前，最好判断一下数据类型
function myIsNaN(value) {
    return typeof value === 'number' && isNaN(value);
}
// 判断NaN更可靠的方法是，利用NaN为唯一不等于自身的值的这个特点，进行判断
function myIsNaN(value) {
    return value !== value;
}

// 5.4 isFinite()
// isFinite方法返回一个布尔值，表示某个值是否为正常的数值
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true

// 除了Infinity、-Infinity、NaN和undefined这几个值会返回false，isFinite对于其他的数值都会返回true
Number.MAX_SAFE_INTEGER
Number.MIN_SAFE_INTEGER